import React, { useState } from 'react';
import {
  TextField,
  Box,
  InputAdornment,
  styled,
} from '@mui/material';
import { Folder as FolderIcon } from '@mui/icons-material';
import { useAppDispatch } from '../../../redux/hooks';
import { openConfirmationDialog } from '../../../redux/slices/generalSlice';
import { NETPYNE_COMMANDS, MODEL_STATE, DEFAULT_CONFIRMATION_DIALOG_MESSAGE } from '../../../constants';
import { PYTHON_CALL } from '../../../constants';
import ActionDialog from './ActionDialog';
import FileBrowser from '../../general/FileBrowser';
import Checkbox from '../../general/Checkbox';
import Tooltip from '../../general/Tooltip';

const StyledContainer = styled(Box)(({ theme }) => ({
  marginTop: theme.spacing(2),
  display: 'flex',
  flexDirection: 'row',
  alignItems: 'baseline',
  width: '100%',
}));

const StyledSelectField = styled(Box)(({ theme }) => ({
  marginTop: theme.spacing(3),
  width: '100%',
}));

const StyledIcon = styled(Box)(({ theme }) => ({
  '&:hover': {
    backgroundColor: 'inherit',
  },
  flex: '0 0 4%',
  marginRight: theme.spacing(2),
  width: theme.typography.h3.fontSize,
  height: theme.typography.h3.fontSize,
  padding: '0px !important',
  zIndex: theme.zIndex.modal,
  cursor: 'pointer',
}));

interface ImportExportHLSDialogProps {
  open?: boolean;
  mode?: 'IMPORT' | 'EXPORT';
  modelState?: string;
  onRequestClose?: () => void;
  openConfirmationDialog?: (payload: any) => void;
  [key: string]: any;
}

/**
 * ImportExportHLSDialog Component
 * Dialog for importing/exporting model from/to Python scripts (HLS - High Level Specification)
 */
const ImportExportHLSDialog: React.FC<ImportExportHLSDialogProps> = ({
  open = false,
  mode = 'IMPORT',
  modelState,
  onRequestClose,
  openConfirmationDialog: propOpenConfirmationDialog,
  ...otherProps
}) => {
  const dispatch = useAppDispatch();
  const [fileName, setFileName] = useState('output');
  const [netParamsPath, setNetParamsPath] = useState('');
  const [netParamsModuleName, setNetParamsModuleName] = useState('');
  const [netParamsVariable, setNetParamsVariable] = useState('netParams');
  const [simConfigPath, setSimConfigPath] = useState('');
  const [simConfigModuleName, setSimConfigModuleName] = useState('');
  const [simConfigVariable, setSimConfigVariable] = useState('simConfig');
  const [modFolder, setModFolder] = useState('');
  const [loadMod, setLoadMod] = useState(false);
  const [compileMod, setCompileMod] = useState(false);
  const [explorerDialogOpen, setExplorerDialogOpen] = useState(false);
  const [explorerParameter, setExplorerParameter] = useState<string>('');
  const [exploreOnlyDirs, setExploreOnlyDirs] = useState(false);
  const [filterFiles, setFilterFiles] = useState<string | boolean>('');
  const [netParamsFullPath, setNetParamsFullPath] = useState('');
  const [simConfigFullPath, setSimConfigFullPath] = useState('');

  const getDirAndModuleFromPath = (fullpath: string): { dirPath: string; moduleName: string } => {
    const fileName = fullpath.replace(/^.*[\\/]/, '');
    const moduleName = fileName.replace(/\.[^/.]+$/, '');
    const dirPath = fullpath.split(fileName).slice(0, -1).join('');
    return { dirPath, moduleName };
  };

  const isFormValid = (): boolean => {
    if (mode === 'IMPORT') {
      return loadMod !== undefined && loadMod !== false;
    }
    return true;
  };

  const showExplorerDialog = (param: string, onlyDirs: boolean, filter: string | boolean): void => {
    setExplorerDialogOpen(true);
    setExplorerParameter(param);
    setExploreOnlyDirs(onlyDirs);
    setFilterFiles(filter);
  };

  const closeExplorerDialog = (fieldValue: any): void => {
    setExplorerDialogOpen(false);
    if (fieldValue) {
      const { dirPath, moduleName } = getDirAndModuleFromPath(fieldValue.path);
      switch (explorerParameter) {
        case 'netParamsPath':
          setNetParamsPath(dirPath);
          setSimConfigPath(dirPath);
          setNetParamsModuleName(moduleName);
          setSimConfigModuleName(moduleName);
          setNetParamsFullPath(fieldValue.path);
          setSimConfigFullPath(fieldValue.path);
          break;
        case 'simConfigPath':
          setSimConfigPath(dirPath);
          setSimConfigModuleName(moduleName);
          setSimConfigFullPath(fieldValue.path);
          break;
        case 'modFolder':
          setModFolder(fieldValue.path);
          setLoadMod(true);
          break;
        default:
          console.error('Not a valid parameter!');
      }
    }
  };

  const onNetParamsPathChange = (fullpath: string): void => {
    const { dirPath, moduleName } = getDirAndModuleFromPath(fullpath);
    setNetParamsPath(dirPath);
    setSimConfigPath(dirPath);
    setNetParamsModuleName(moduleName);
    setSimConfigModuleName(moduleName);
    setNetParamsFullPath(fullpath);
    setSimConfigFullPath(fullpath);
  };

  const onSimConfigPathChange = (fullpath: string): void => {
    const { dirPath, moduleName } = getDirAndModuleFromPath(fullpath);
    setSimConfigPath(dirPath);
    setSimConfigModuleName(moduleName);
    setSimConfigFullPath(fullpath);
  };

  const onModFolderPathChange = (fullpath: string): void => {
    setModFolder(fullpath);
    setLoadMod(fullpath !== '');
  };

  const handleConfirmation = (command: string, args: any): void => {
    const confirmPayload = {
      title: 'Warning',
      message: DEFAULT_CONFIRMATION_DIALOG_MESSAGE,
      onConfirm: {
        type: PYTHON_CALL,
        cmd: command,
        args,
      },
    };

    if (propOpenConfirmationDialog) {
      propOpenConfirmationDialog(confirmPayload);
    } else {
      dispatch(openConfirmationDialog(confirmPayload));
    }
  };

  const args = {
    fileName,
    netParamsPath,
    netParamsModuleName,
    netParamsVariable,
    simConfigPath,
    simConfigModuleName,
    simConfigVariable,
    modFolder,
    loadMod,
    compileMod,
  };

  let command: string;
  let message: string;
  let buttonLabel: string;
  let title: string;
  let content: React.ReactNode;
  let callback: ((cmd: string, args: any) => void) | undefined;

  if (mode === 'IMPORT') {
    command = NETPYNE_COMMANDS.importModel;
    message = 'IMPORTING MODEL';
    buttonLabel = 'Import';
    title = 'Import from Python scripts';
    callback =
      modelState === MODEL_STATE.INSTANTIATED || modelState === MODEL_STATE.SIMULATED
        ? (cmd: string, args: any) => handleConfirmation(cmd, args)
        : undefined;

    content = (
      <>
        <TextField
          variant="filled"
          fullWidth
          value={netParamsFullPath}
          onChange={(e) => onNetParamsPathChange(e.target.value)}
          label="NetParams file"
          helperText="Only .py files"
          InputProps={{
            startAdornment: (
              <InputAdornment position="start">
                <Tooltip title="File explorer" placement="top">
                  <FolderIcon
                    onClick={() => showExplorerDialog('netParamsPath', false, '.py')}
                    sx={{ cursor: 'pointer' }}
                  />
                </Tooltip>
              </InputAdornment>
            ),
          }}
        />

        <Box mt={1} mb={2}>
          <TextField
            variant="filled"
            fullWidth
            label="NetParams variable"
            value={netParamsVariable}
            onChange={(e) => setNetParamsVariable(e.target.value)}
          />
        </Box>

        <TextField
          variant="filled"
          fullWidth
          value={simConfigFullPath}
          onChange={(e) => onSimConfigPathChange(e.target.value)}
          label="SimConfig file:"
          helperText="Only .py files"
          InputProps={{
            startAdornment: (
              <InputAdornment position="start">
                <Tooltip title="File explorer" placement="top">
                  <FolderIcon
                    onClick={() => showExplorerDialog('simConfigPath', false, '.py')}
                    sx={{ cursor: 'pointer' }}
                  />
                </Tooltip>
              </InputAdornment>
            ),
          }}
        />

        <Box mt={1} mb={2}>
          <TextField
            variant="filled"
            fullWidth
            label="SimConfig variable"
            value={simConfigVariable}
            onChange={(e) => setSimConfigVariable(e.target.value)}
          />
        </Box>

        <TextField
          variant="filled"
          fullWidth
          label="Path to mod files"
          value={modFolder}
          onChange={(e) => onModFolderPathChange(e.target.value)}
          helperText="Important: if external mod files are required please select the mod folder path"
          InputProps={{
            startAdornment: (
              <InputAdornment position="start">
                <Tooltip title="File explorer" placement="top">
                  <FolderIcon
                    onClick={() => showExplorerDialog('modFolder', true, false)}
                    sx={{ cursor: 'pointer' }}
                  />
                </Tooltip>
              </InputAdornment>
            ),
          }}
        />

        <Checkbox
          fullWidth
          noBackground
          label="Compile mod files"
          checked={compileMod}
          onChange={() => setCompileMod(!compileMod)}
        />
      </>
    );
  } else {
    // EXPORT mode
    command = NETPYNE_COMMANDS.exportHLS;
    message = 'EXPORTING MODEL';
    buttonLabel = 'Export';
    title = 'Export as Python script';

    content = (
      <TextField
        variant="filled"
        fullWidth
        label="File name"
        value={fileName}
        onChange={(e) => setFileName(e.target.value)}
      />
    );
  }

  return (
    <>
      <ActionDialog
        command={command}
        message={message}
        buttonLabel={buttonLabel}
        args={args}
        title={title}
        isFormValid={isFormValid}
        callback={callback}
        open={open}
        onRequestClose={onRequestClose}
        {...otherProps}
      >
        {content}
      </ActionDialog>

      {mode === 'IMPORT' && (
        <FileBrowser
          open={explorerDialogOpen}
          exploreOnlyDirs={exploreOnlyDirs}
          filterFiles={filterFiles}
          onRequestClose={closeExplorerDialog}
        />
      )}
    </>
  );
};

export default ImportExportHLSDialog;

